from minio import Minio
from minio.error import S3Error
from typing import Optional, BinaryIO
from app.core.config import settings
import os
import uuid
import logging

logger = logging.getLogger(__name__)


class StorageManager:
    """存储管理器，负责处理对象存储操作"""
    
    def __init__(self):
        # 初始化MinIO客户端
        self.client = Minio(
            settings.MINIO_ENDPOINT,
            access_key=settings.MINIO_ACCESS_KEY,
            secret_key=settings.MINIO_SECRET_KEY,
            secure=settings.MINIO_SECURE
        )
        
        # 确保存储桶存在
        self.ensure_bucket_exists()
    
    def ensure_bucket_exists(self):
        """确保存储桶存在，如果不存在则创建"""
        try:
            if not self.client.bucket_exists(settings.MINIO_BUCKET_NAME):
                self.client.make_bucket(settings.MINIO_BUCKET_NAME)
                logger.info(f"Created bucket: {settings.MINIO_BUCKET_NAME}")
            else:
                logger.info(f"Bucket already exists: {settings.MINIO_BUCKET_NAME}")
        except S3Error as e:
            logger.error(f"Error creating bucket: {e}")
            raise
    
    def upload_file(self, file_path: str, object_name: Optional[str] = None, content_type: str = "application/octet-stream") -> str:
        """上传文件到MinIO
        
        Args:
            file_path: 本地文件路径
            object_name: 对象名称，如果为None则自动生成
            content_type: 内容类型
            
        Returns:
            str: 上传后的对象名称
        """
        try:
            # 如果没有提供对象名称，则生成一个唯一的名称
            if not object_name:
                object_name = f"{uuid.uuid4()}"
            
            # 获取文件大小
            file_size = os.path.getsize(file_path)
            
            # 上传文件
            self.client.fput_object(
                bucket_name=settings.MINIO_BUCKET_NAME,
                object_name=object_name,
                file_path=file_path,
                content_type=content_type
            )
            
            logger.info(f"Uploaded file {file_path} to {object_name}")
            return object_name
            
        except S3Error as e:
            logger.error(f"Error uploading file: {e}")
            raise
    
    def upload_fileobj(self, file_obj: BinaryIO, object_name: str, content_type: str = "application/octet-stream") -> str:
        """上传文件对象到MinIO
        
        Args:
            file_obj: 文件对象
            object_name: 对象名称
            content_type: 内容类型
            
        Returns:
            str: 上传后的对象名称
        """
        try:
            # 获取文件大小
            file_obj.seek(0, os.SEEK_END)
            file_size = file_obj.tell()
            file_obj.seek(0)
            
            # 上传文件
            self.client.put_object(
                bucket_name=settings.MINIO_BUCKET_NAME,
                object_name=object_name,
                data=file_obj,
                length=file_size,
                content_type=content_type
            )
            
            logger.info(f"Uploaded file object to {object_name}")
            return object_name
            
        except S3Error as e:
            logger.error(f"Error uploading file object: {e}")
            raise
    
    def download_file(self, object_name: str, file_path: str) -> bool:
        """从MinIO下载文件
        
        Args:
            object_name: 对象名称
            file_path: 本地文件路径
            
        Returns:
            bool: 是否成功下载
        """
        try:
            self.client.fget_object(
                bucket_name=settings.MINIO_BUCKET_NAME,
                object_name=object_name,
                file_path=file_path
            )
            
            logger.info(f"Downloaded file {object_name} to {file_path}")
            return True
            
        except S3Error as e:
            logger.error(f"Error downloading file: {e}")
            return False
    
    def delete_file(self, object_name: str) -> bool:
        """从MinIO删除文件
        
        Args:
            object_name: 对象名称
            
        Returns:
            bool: 是否成功删除
        """
        try:
            self.client.remove_object(
                bucket_name=settings.MINIO_BUCKET_NAME,
                object_name=object_name
            )
            
            logger.info(f"Deleted file: {object_name}")
            return True
            
        except S3Error as e:
            logger.error(f"Error deleting file: {e}")
            return False
    
    def get_presigned_url(self, object_name: str, expires_in: int = 3600) -> str:
        """获取预签名URL，用于临时访问对象
        
        Args:
            object_name: 对象名称
            expires_in: URL过期时间（秒）
            
        Returns:
            str: 预签名URL
        """
        try:
            url = self.client.presigned_get_object(
                bucket_name=settings.MINIO_BUCKET_NAME,
                object_name=object_name,
                expires=expires_in
            )
            
            return url
            
        except S3Error as e:
            logger.error(f"Error generating presigned URL: {e}")
            raise
    
    def list_objects(self, prefix: Optional[str] = None) -> list:
        """列出存储桶中的对象
        
        Args:
            prefix: 对象名称前缀
            
        Returns:
            list: 对象列表
        """
        try:
            objects = self.client.list_objects(
                bucket_name=settings.MINIO_BUCKET_NAME,
                prefix=prefix,
                recursive=True
            )
            
            return [obj.object_name for obj in objects]
            
        except S3Error as e:
            logger.error(f"Error listing objects: {e}")
            return []
    
    def get_object_metadata(self, object_name: str) -> dict:
        """获取对象的元数据
        
        Args:
            object_name: 对象名称
            
        Returns:
            dict: 对象元数据
        """
        try:
            response = self.client.stat_object(
                bucket_name=settings.MINIO_BUCKET_NAME,
                object_name=object_name
            )
            
            return {
                "size": response.size,
                "last_modified": response.last_modified,
                "content_type": response.content_type
            }
            
        except S3Error as e:
            logger.error(f"Error getting object metadata: {e}")
            raise


# 创建存储管理器实例
storage_manager = StorageManager()